package edu.monash.historyofadoption;

/*
 * MfClient.java
 *
 * Created on August 19, 2002, 12:27 AM
 */

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import arc.mf.client.RemoteServer;
import arc.streams.LongInputStream;
import arc.streams.StreamCopy;
import arc.xml.XmlDoc;
import arc.xml.XmlStringWriter;

/**
 * 
 * @author jason lohrey
 * @version
 * 
 *          This sample client illustrates making calles to Mediaflux to create
 *          an asset, execute a query and rerieve assets in the query.
 */
public class MediafluxClient {

	public MediafluxClient(MediafluxConfig mfConfig) throws Throwable {
		super();
		this.mfConfig = mfConfig;

		// Create an instance of the server.
		RemoteServer server = new RemoteServer(mfConfig.getHost(), mfConfig
				.getPort(), mfConfig.isUseSSL(), mfConfig.isEncrypt());

		// Create a connection to the server. Connections are pooled, so they
		// should be closed when finished with. Alternatively, they may be
		// kept open as long as required.
		this.cxn = (RemoteServer.Connection) server.open();

		// We, the client, are required to log into the server before we can
		// execute services.
		this.cxn = (RemoteServer.Connection) server.open();

		this.cxn.connect(mfConfig.getDomain(), mfConfig.getUser(), mfConfig
				.getPassword());
	}

	public MediafluxConfig getMfConfig() {
		return mfConfig;
	}

	MediafluxConfig mfConfig;
	RemoteServer.Connection cxn;

	/**
	 * Execute a query to locate collections off the root collection (from
	 * config file).
	 */
	public Collection<XmlDoc.Element> getBaseCollectionList() throws Throwable {

		// Create the arguments document for the service. We'll ask for the
		// meta information for each asset to be returned - the default is to
		// simply return the asset identifier (which is equivalient to an
		// action of 'get-id'):
		XmlStringWriter w = new XmlStringWriter();
		w.add("namespace", this.mfConfig.getBasecollection());

		// Execute the service 'asset.create' with the arguments and the (data)
		// input.
		// There is no (data) output for this service.
		XmlDoc.Element reply = cxn.execute("asset.namespace.list",
				w.document(), null, null);

		// Iterate through the results, and display each asset.
		Collection collectionlist = reply.elements("namespace/namespace");
		if (collectionlist == null) {
			return new Vector<XmlDoc.Element>();
		} else {
			return collectionlist;
		}
	}

	/**
	 * Execute a query to locate assets.
	 */
	public Collection<XmlDoc.Element> getAssetList(String namespace)
			throws Throwable {

		// Create a query..
		String where = "namespace=" + "\'" + mfConfig.getBasecollection() + "/"
				+ namespace + "\'";

		// Create the arguments document for the service. We'll ask for the
		// meta information for each asset to be returned - the default is to
		// simply return the asset identifier (which is equivalient to an
		// action of 'get-id'):
		XmlStringWriter w = new XmlStringWriter();
		w.add("where", where);
		w.add("action", "get-meta");

		// Execute the service 'asset.create' with the arguments and the (data)
		// input.
		// There is no (data) output for this service.
		XmlDoc.Element reply = cxn.execute("asset.query", w.document(), null,
				null);

		// Iterate through the results, and display each asset.
		Collection<XmlDoc.Element> assetlist = reply.elements("asset");
		if (assetlist == null) {
			return new Vector<XmlDoc.Element>();
		} else {
			return assetlist;
		}

	}

	/**
	 * "Display" each asset.
	 */
	public String getNamespace(XmlDoc.Element e) throws Throwable {

		// String namespace = e.value("@namespace");
		String namespace = e.value();

		// Display a few of the nodes for the asset node:
		return namespace;

	}

	/**
	 * "Display" each asset. EXAMPLE METHOD
	 */
	public void displayAssets(Collection<XmlDoc.Element> assetlist,
			PrintWriter out) throws Throwable {

		Iterator<XmlDoc.Element> it = assetlist.iterator();
		out.println("<ul>");
		while (it.hasNext()) {
			XmlDoc.Element e = (XmlDoc.Element) it.next();
			String assetName = getAssetName(e);
			if (!assetName.equals( "metadata.txt")) {
				out.println("<li>Asset id: " + getAssetId(e) + " <br/>Asset name: "
						+ assetName + " <br/>type: " + getAssetType(e));
				displayAssetContentInfo(getAssetContents(getAssetId(e)), out);
				out.println("</li>");
			}
		}
		out.println("</ul>");

	}

	public void displayAssetSelect(Collection<XmlDoc.Element> assetlist,
			PrintWriter out) throws Throwable {

		Iterator<XmlDoc.Element> it = assetlist.iterator();
		out.println("<p>");
		while (it.hasNext()) {
			XmlDoc.Element e = (XmlDoc.Element) it.next();
			String assetName = getAssetName(e);
			if (!assetName.equals( "metadata.txt")) {
				out.println("<input type='checkbox' name='assets' value='" + getAssetId(e) + "'>"
						+ assetName + " (type: " + getAssetType(e) + ")<br/>");
				//displayAssetContentInfo(getAssetContents(getAssetId(e)), out);
				//out.println("</li>");
			}
		}
		out.println("</p>");

	}
	
	/**
	 * Get asset's ID from its XML element
	 */
	public String getAssetId(XmlDoc.Element assetElement) {
		try {
			return assetElement.value("@id");
		} catch (Throwable e) {
			return "";
		}
	}

	/**
	 * Get asset's Name from its XML element
	 */
	public String getAssetName(XmlDoc.Element assetElement) {
		try {
			return assetElement.value("name");
		} catch (Throwable e) {
			return "";
		}
	}

	/**
	 * Get asset's Type from its XML element
	 */
	public String getAssetType(XmlDoc.Element assetElement) {
		try {
			return assetElement.value("type");
		} catch (Throwable e) {
			return "";
		}
	}

	/**
	 * "Display" each asset's content information. EXAMPLE METHOD
	 */
	public void displayAssetContentInfo(MemoryOutput out, PrintWriter output) {
		// The data is now in the output buffer... we could choose to do
		// anything
		// we like with it (e.g. display).
		output.println("  <br/>Content type: " + out.type()
				+ " <br/>size (bytes): " + out.size());
	}

	public MemoryOutput getAssetContents(String assetId) throws Throwable {
		// Output is retrieved using an arc.ServerClient.Output. It is the
		// responsibility of the client to subclass this interface to provide
		// the necessary output.
		MemoryOutput out = new MemoryOutput();

		XmlStringWriter w = new XmlStringWriter();
		w.add("id", assetId);

		// Execute the service 'asset.get' with the arguments and the (data)
		// output.
		// There is no (data) input for this service. We are not really
		// interested
		// in the reply.
		cxn.execute("asset.get", w.document(), null, out);

		return out;

	}

	// -------------------------------------------------------------------------
	// Class: MemoryOutput
	// -------------------------------------------------------------------------

	 static class MemoryOutput implements
			arc.mf.client.RemoteServer.Output {

		/**
		 * Constructor
		 */
		public MemoryOutput() {
			_data = null;
			_type = null;
		}

		/**
		 * Returns the MIME type of the asset.
		 */
		public String type() {
			return _type;
		}

		/**
		 * Returns the length (in bytes) of the data.
		 */
		public long size() {
			return (long) _data.size();
		}

		// ---------------------------------------------------------------------
		// Interface: arc.Server.Client.Output
		// ---------------------------------------------------------------------

		/**
		 * Minimum number of outputs expected.
		 */
		public int min() {
			return 1;
		}

		/**
		 * Maximum number of outputs that can be consumed.
		 */
		public int max() {
			return 1;
		}

		/**
		 * Indicates the number of outputs to be written.
		 */
		public void setNbOutputs(int nb) {
		}

		/**
		 * Copies the input (output) stream to this output.
		 */
		public boolean copy(LongInputStream in, String type) throws Throwable {
			_type = type;

			// Create a buffer to hold the data..
			_data = new ByteArrayOutputStream((int) in.length());

			// Use the arc stream copy utility to copy the input into our
			// memory buffer.
			StreamCopy.copy(in, _data, in.length());

			// This method did consume the data.
			return true;
		}

		/**
		 * Returns the stream associated with the output. That is, the data may
		 * be obtained on this stream.
		 */
		public LongInputStream stream() throws Throwable {
			return null;
		}

		public byte[] bytearray() {
			return _data.toByteArray();
		}
		
		/**
		 * Close the output.
		 */
		public void close() {
		}

		// ---------------------------------------------------------------------
		// Member variables:
		// ---------------------------------------------------------------------

		private ByteArrayOutputStream _data;
		private String _type;
	}

}